package edu.stanford.bmir.protege.web.server.stkosutil.cutter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Stack;

import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.EntityType;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.RemoveAxiom;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;

import edu.stanford.bmir.protege.web.client.rpc.data.EntityData;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.ItemSelector;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.MatchLevel;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.UtilConstants;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingMode;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingScope;
import edu.stanford.bmir.protege.web.server.owlapi.AssertedClassHierarchyProvider;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProject;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProjectManager;
import edu.stanford.bmir.protege.web.server.owlapi.RenderingManager;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlClassHolder;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlIndividualHolder;
import edu.stanford.bmir.protege.web.shared.project.ProjectId;
import edu.stanford.bmir.protege.web.shared.user.UserId;

public class IndividualCutter {

	/****
	 * The following functions are copied from OntologyServiceOWLAPIImpl as they
	 * are not visible
	 * 
	 */
	private OWLAPIProject getProject(String projectName) {
		if (projectName == null) {
			throw new NullPointerException("projectName must not be null");
		}
		ProjectId projectId = ProjectId.get(projectName);
		// TODO: Log
		return getProject(projectId);
	}

	/**
	 * Gets the OWLAPIProject for a given {@link ProjectId}.If a project with
	 * the specified id exists then that project will be returned, otherwise, a
	 * fresh project will be created and that fresh project returned.
	 * 
	 * @param projectId
	 *            The id of the project.
	 * @return The OWL API project. Not <code>null</code>.
	 */
	private OWLAPIProject getProject(ProjectId projectId) {
		OWLAPIProjectManager pm = OWLAPIProjectManager.getProjectManager();
		return pm.getProject(projectId);
	}

	// *******************

	private List<OwlClassHolder> getSubclasses(OWLAPIProject project,
			String className) {
		if (className == null) {
			return Collections.emptyList();
		}
		List<OwlClassHolder> result = new ArrayList<OwlClassHolder>();

		RenderingManager rm = project.getRenderingManager();
		AssertedClassHierarchyProvider hierarchyProvider = project
				.getClassHierarchyProvider();
		OWLClass cls = rm.getEntity(className, EntityType.CLASS);

		boolean checkForDeprecated = project.getRootOntology()
				.containsAnnotationPropertyInSignature(
						OWLRDFVocabulary.OWL_DEPRECATED.getIRI());
		for (OWLClass subclass : new ArrayList<OWLClass>(
				hierarchyProvider.getChildren(cls))) {
			boolean deprecated = false;
			// if(checkForDeprecated) {
			// deprecated = project.isDeprecated(subclass);
			// }
			// if (!deprecated) {
			// Set<OWLClass> children = hierarchyProvider.getChildren(subclass);
			// int subClassSubClassesCount = children.size();
			String browserText = rm.getBrowserText(subclass);
			String name = subclass.getIRI().toString();
			OwlClassHolder classHolder = new OwlClassHolder();
			;
			classHolder.setBrowserText(browserText);
			classHolder.setName(name);
			classHolder.setOwlClass(subclass);
			// data.setDeprecated(deprecated);

			// data.setValueType(ValueType.Cls);
			result.add(classHolder);
			// }
		}
		Collections.sort(result, new Comparator<OwlClassHolder>() {
			public int compare(OwlClassHolder o1, OwlClassHolder o2) {
				// if(o1.isDeprecated()) {
				// if(!o2.isDeprecated()) {
				// return 1;
				// }
				// }
				// else if(o2.isDeprecated()) {
				// return -1;
				// }
				String browserText1 = o1.getBrowserText();
				String browserText2 = o2.getBrowserText();
				if (browserText1.startsWith("'")) {
					browserText1 = browserText1.substring(1);
				}
				if (browserText2.startsWith("'")) {
					browserText2 = browserText2.substring(1);
				}
				return browserText1.compareToIgnoreCase(browserText2);
			}
		});
		return result;
	}

	private List<OwlClassHolder> loadAllClassList(OWLAPIProject project,
			String topClassName) {
		List<OwlClassHolder> result = new ArrayList<OwlClassHolder>();
		Stack<OwlClassHolder> sClassHolders = new Stack<OwlClassHolder>();
		OwlClassHolder root = this.getTopClass(project, topClassName);
		sClassHolders.push(root);
		while (!sClassHolders.empty()) {
			OwlClassHolder currentClass = sClassHolders.pop();
			List<OwlClassHolder> children = this.getSubclasses(project,
					currentClass.getName());
			Collections.reverse(children);
			for (OwlClassHolder child : children) {
				sClassHolders.push(child);
			}
			result.add(currentClass);
		}
		return result;
	}
	private List<OwlIndividualHolder> loadAllIndividualList(OWLAPIProject project, List<OwlClassHolder> classList){
		List<OwlIndividualHolder> result = new ArrayList<OwlIndividualHolder>();
		HashMap<String, OwlIndividualHolder> hHolders = new HashMap<String, OwlIndividualHolder>();
		for (OwlClassHolder classHolder: classList){
			if (classHolder.getOwlClass().getIRI().equals(OWLRDFVocabulary.OWL_THING.getIRI())){
				continue;
			}
			List<OwlIndividualHolder> tempIndividualList = this.fetchIndividuals(project, classHolder.getOwlClass());
			//result.addAll(tempIndividualList);
			for (OwlIndividualHolder tempIndividual: tempIndividualList){
				hHolders.put(tempIndividual.getName(), tempIndividual);
			}
		}
		Collection<OwlIndividualHolder> cTemp = hHolders.values();
		for (OwlIndividualHolder holder: cTemp){
			result.add(holder);
		}		
		return result;
	}

	private OwlClassHolder getTopClass(OWLAPIProject project,
			String topClassName) {
		OwlClassHolder holder = new OwlClassHolder();
		RenderingManager rm = project.getRenderingManager();
		OWLClass topClass = null;
		if (topClassName == null) {
			topClass = project.getDataFactory().getOWLThing();
		} else {
			topClass = rm.getEntity(topClassName, EntityType.CLASS);
		}
		String browserText = rm.getBrowserText(topClass);
		String name = topClass.getIRI().toString();
		holder.setBrowserText(browserText);
		holder.setName(name);
		holder.setOwlClass(topClass);
		return holder;
	}

	public CuttingResult cutOntology(String projectName,
			CuttingOptions options, UserId userId) {
		String refProjectName = options.getRefProjectName();
		CuttingResult result = new CuttingResult();
		List<OwlClassHolder> oClassList = null;

		List<OWLOntologyChange> oChangeList = new ArrayList<OWLOntologyChange>();

		OWLAPIProject currProject = this.getProject(projectName);
		// OWLAPIProject refProject = this.getProject(refProjectName);
		String topClassName = null;
		if (options.getCuttingScope() == CuttingScope.SELECTED_CLASS_TREE) {
			topClassName = options.getSelectedEntityName();
		}

		oClassList = this.loadAllClassList(currProject, topClassName);
		List<OwlIndividualHolder> lIndividualList = this.loadAllIndividualList(currProject, oClassList);
		int cutNumber = this.fillCuttingFlagAxiom(currProject, lIndividualList,
				oChangeList, options);
		currProject.applyChanges(userId, oChangeList, "本体自动裁切");
		result.setCutClassNumber(cutNumber);
		result.setTotalClassNumber(oClassList.size());
		return result;
	}

	private int fillCuttingFlagAxiom(OWLAPIProject project,
			List<OwlIndividualHolder> individualList, List<OWLOntologyChange> changeList,
			CuttingOptions options) {
		int count = 0;
		OWLDataFactory df = project.getDataFactory();
		for (OwlIndividualHolder owlIndividualHolder : individualList) {
			OWLNamedIndividual owlIndividual = owlIndividualHolder.getOwlNamedIndividual();
			boolean hasMappingNo = false;

			for (OWLAnnotation annotation : owlIndividual.getAnnotations(
					project.getRootOntology(), df.getRDFSComment())) {
				if (annotation.getValue() instanceof OWLLiteral) {
					OWLLiteral commentValue = (OWLLiteral) annotation
							.getValue();
					String value = commentValue.getLiteral();
					if (value.startsWith(UtilConstants.FLAG_MAPPING_NO)) {
						hasMappingNo = true;
					}
					if (value.startsWith(UtilConstants.FLAG_CUTTING)) {
						OWLAxiom oldAx = df.getOWLAnnotationAssertionAxiom(
								owlIndividual.getIRI(), annotation);
						RemoveAxiom removeAxiom = new RemoveAxiom(
								project.getRootOntology(), oldAx);
						changeList.add(removeAxiom);
					}
				}
			}// end for

			if (qualifiedByCondition(owlIndividualHolder, hasMappingNo, options)) {
				String annotaionValue = UtilConstants.FLAG_CUTTING + ": " + "Y";
				OWLAnnotation commentAnno = df.getOWLAnnotation(
						df.getRDFSComment(),
						df.getOWLLiteral(annotaionValue, "en"));
				OWLAxiom ax = df.getOWLAnnotationAssertionAxiom(
						owlIndividual.getIRI(), commentAnno);
				AddAxiom addAxiom = new AddAxiom(project.getRootOntology(), ax);
				changeList.add(addAxiom);
				count++;
			}

		}// end for
		return count;
	}

	private boolean qualifiedByCondition(OwlIndividualHolder individualHolder,
			boolean hasMappingNo, CuttingOptions options) {
		if (options.getCuttingMode() == CuttingMode.REF_PROJECT) {
			boolean qualified = (hasMappingNo && options.getItemSelector() == ItemSelector.MATCHED_ITEMS)
					|| (hasMappingNo == false && options.getItemSelector() == ItemSelector.NOT_MATCHED_ITEMS);
			return qualified;
		} else {
			boolean matched = false;
			String search = options.getSearch();
			String entityName = individualHolder.getBrowserText();
			if (search.compareTo("*") == 0) {
				return options.getItemSelector() == ItemSelector.MATCHED_ITEMS;
			}
			if (options.getMatchLevel() == MatchLevel.CASE_SENSITIVE) {
				matched = entityName.compareTo(search) == 0;
			} else if (options.getMatchLevel() == MatchLevel.CASE_INSENSITIVE) {
				matched = entityName.compareToIgnoreCase(search) == 0;
			} else if (options.getMatchLevel() == MatchLevel.INCLUSIVE) {
				search = search.toLowerCase();
				entityName = entityName.toLowerCase();
				if (entityName.indexOf(search) != -1) {
					matched = true;
				} else {
					matched = false;
				}
			}
			if (options.getItemSelector() == ItemSelector.MATCHED_ITEMS) {
				return matched;
			} else {
				return !matched;
			}
		}
	}
	private List<OwlIndividualHolder> fetchIndividuals(OWLAPIProject project, OWLClass cls){
		List<OwlIndividualHolder> result = new ArrayList<OwlIndividualHolder>();
		RenderingManager rm = project.getRenderingManager();
        OWLOntology rootOntology = project.getRootOntology();

        if(cls.getIRI().equals(OWLRDFVocabulary.OWL_THING.getIRI())) {
            for (OWLOntology ont : rootOntology.getImportsClosure()) {
                for(OWLNamedIndividual namedIndividual : ont.getIndividualsInSignature()) {
                	OwlIndividualHolder holder = new OwlIndividualHolder();
                	String browserText = rm.getBrowserText(namedIndividual);
                	holder.setOwlNamedIndividual(namedIndividual);
                	holder.setName(namedIndividual.getIRI().toString());
                	holder.setBrowserText(browserText);
                    result.add(holder);
                }
            }
        }
        else {
            for(OWLIndividual individual : cls.getIndividuals(rootOntology.getImportsClosure())) {
                if(individual.isAnonymous()) {
                   // entityData = rm.getEntityData(individual.asOWLAnonymousIndividual());
                }
                else {
                	OWLNamedIndividual namedIndividual = (OWLNamedIndividual)individual;
                	OwlIndividualHolder holder = new OwlIndividualHolder();
                	String browserText = rm.getBrowserText(namedIndividual);
                	holder.setOwlNamedIndividual(namedIndividual);
                	holder.setName(namedIndividual.getIRI().toString());
                	holder.setBrowserText(browserText);
                	result.add(holder);
                }
                
            }
        }

        return result;
	}
}
